package environment;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.ListIterator;
import java.util.Map;

import Operator.Operator;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.Thread;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.AbstractTableModel;

import com.approximatrix.charting.CoordSystem;
import com.approximatrix.charting.DefaultChart;
import com.approximatrix.charting.model.MultiScatterDataModel;
import com.approximatrix.charting.model.ObjectChartDataModel;
import com.approximatrix.charting.render.BarChartRenderer;
import com.approximatrix.charting.render.MultiScatterChartRenderer;
import com.approximatrix.charting.render.PieChartRenderer;
import com.approximatrix.charting.swing.ChartPanel;

public class EnviromentAgent extends JFrame {
	/**
	 * 
	 */
	private static final long serialVersionUID = -892984402589782967L;

	// Gui stuff
	private JPanel mainPanel;
	private JTable operatorsTable;
	private MyTableModel operatorsTableModel;
	private ChartPanel chartPanel;
	private JTextArea operatorPrints;
	private JTextArea clientPrints;
	private JButton stopButton;
	private volatile boolean run = true;
	private Thread mainThread; /// here the main program loop will be executed
	private MarketAgent market_agent;

	private JPanel chartHostPanel;
	private JPanel plotHostPanel;
	
	private int PLOT_HISTORY = 12;
	
	private int chart_data[][];
	
	public EnviromentAgent() {
		super("mobiA");
		setPreferredSize(new Dimension(1000, 700));
		setSize(new Dimension(1000, 700));

		mainThread = new Thread( new mainLoop() );
		mainPanel = new JPanel(new GridBagLayout());

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		GridBagConstraints gb = new GridBagConstraints();
		gb.insets = new Insets(10, 10, 10, 10);
		gb.gridx = 0;
		gb.gridy = 0;
		gb.weightx = 1.0;
		gb.weighty = 0.2;
		gb.gridwidth = 2;
		gb.fill = GridBagConstraints.BOTH;
		
		operatorsTable = new JTable();
		operatorsTable.setFillsViewportHeight(true);
		operatorsTable.setAutoCreateRowSorter(true);
		JScrollPane jspT = new JScrollPane(operatorsTable);
		mainPanel.add(jspT, gb);
		
		gb.gridx = 1;
		gb.gridy = 1;

		gb.gridwidth =1;
		gb.weightx = 0.3;
		gb.fill = GridBagConstraints.BOTH;
		chartHostPanel = new JPanel(new BorderLayout(10,10));
		chartHostPanel.add(new JLabel("SUF"));
		chartHostPanel.setSize(new Dimension(300, 300));
		chartHostPanel.setPreferredSize(new Dimension(300, 300));
		chartHostPanel.setMinimumSize(new Dimension(300, 300));
		mainPanel.add(chartHostPanel, gb);
		
		plotHostPanel = new JPanel(new BorderLayout(10,10));

		gb.gridx = 0;
		gb.gridy = 1;
		gb.weighty = 0.1; 
		gb.weightx = 0.7;
		
		plotHostPanel.add(new JLabel("SUF"));
		plotHostPanel.setSize(new Dimension(300, 300));
		plotHostPanel.setPreferredSize(new Dimension(300, 300));
		plotHostPanel.setMinimumSize(new Dimension(300, 300));
		mainPanel.add(plotHostPanel,gb);

		gb.gridx = 1;
		gb.gridy = 3;

		gb.gridwidth = 1;
		gb.weightx = 0.0;
		gb.weighty = 0.0;
		stopButton = new JButton("Pause");

		//gb.fill = GridBagConstraints.NONE;
		stopButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (run)
					stopButton.setText("Run");
				else
					stopButton.setText("Pause");
				run = !run;
				market_agent.switchRun();
			}
		});
		mainPanel.add(stopButton, gb);


		gb.fill = GridBagConstraints.BOTH;
		gb.gridx = 0;
		gb.gridy = 2;
		gb.weightx = 0.5;
		gb.weighty = 0.3;
		operatorPrints = new JTextArea(5, 30);
		operatorPrints.setEditable(false);
		operatorPrints.setWrapStyleWord(true);
		operatorPrints.setLineWrap(true);

		clientPrints = new JTextArea(5, 300);
		clientPrints.setEditable(false);
		clientPrints.setWrapStyleWord(true);
		clientPrints.setLineWrap(true);

		JScrollPane jsp1 = new JScrollPane(operatorPrints);
		jsp1.setSize(300, 400);
		mainPanel.add(jsp1, gb);

		gb.gridx = 1;
		JScrollPane jsp2 = new JScrollPane(clientPrints);
		jsp2.setPreferredSize(new Dimension(600, 200));

		mainPanel.add(jsp2, gb);

		setContentPane(mainPanel);
		// pack();
	}
	
	class MyTableModel extends AbstractTableModel {
		private static final long serialVersionUID = 5814882632405043865L;
		private String[] columnNames = {"nazwa", "użytkowników", "zwykłe", "do play", "w sieci", "sms", "sms w sieci", "ostatnia strategia"};
		private Object[][] data;
		
	    MyTableModel()
	    {
	    	super();
	    	reload();
	    }
	    
	    public void reload()
	    {
	    	Map<String, Operator> operators = market_agent.getOperators();
	    	data = new Object[ operators.size() ][ columnNames.length ];
	    	
	    	int i = 0;
			for (Operator op : operators.values())
			{
				data[i][0] = op.getName();
				data[i][1] = op.getClientCount();
				double [] prices = op.getPrices();
				data[i][2] = prices[0];
				data[i][3] = prices[1];
				data[i][4] = prices[2];
				data[i][5] = prices[3];
				data[i][6] = prices[4];
				data[i][7] = op.getLastStrategyUsed();
				
				i++;
			}
	    }
	    
	    public int getColumnCount() {
	        return columnNames.length;
	    }

	    public int getRowCount() {
	        return data.length;
	    }

	    public String getColumnName(int col) {
	        return columnNames[col];
	    }

	    public Object getValueAt(int row, int col) {
	        return data[row][col];
	    }

	    public Class getColumnClass(int c) {
	        return getValueAt(0, c).getClass();
	    }

	    /*
	     * Don't need to implement this method unless your table's
	     * data can change.
	     */
	    public void setValueAt(Object value, int row, int col) {
	        data[row][col] = value;
	        fireTableCellUpdated(row, col);
	    }
	}
	
	private double plotData[][];
	private ChartPanel plotPanel;
	
	private void refreshPlot()
	{
		Map<String, Integer> op_count = market_agent.getOperatorsCount();
		int i = 0;
		int j = PLOT_HISTORY - 2;
		
		while ( j >=0 )
		{
			for(int z = 0; z < op_count.size(); z++)
			{
				plotData[z][j+1] = plotData[z][j];
			}
			
			j--;
		}
		
		i = 0;
		for (String op : op_count.keySet())
		{
			plotData[i][0] = op_count.get(op);
			i++;
		}
	}
	double [] months;
	private void initPlot()
	{
		
		MultiScatterDataModel model = new MultiScatterDataModel();
		
		Map<String, Operator> operators = market_agent.getOperators();
		
		if (plotData == null)
			plotData = new double[operators.size()][PLOT_HISTORY];
		
		refreshPlot();
		
		if (months == null || true)
		{
			months = new double[PLOT_HISTORY];
		
			for(int i=0; i< PLOT_HISTORY; i++) {
				months[i] = i;
			}
		}
		else
		{
			double j = months[1];
			for(int i = 0; i< PLOT_HISTORY; i++) {
				months[(int) i] = j;
				j += 1.0;
				if( j > 12)
					j = 1;
			}
		}
		
		int i = 0;
		for(Operator op : operators.values()) {
	        // Add the data to the model
	        model.addData(months, plotData[i], op.getName());
	        
	        // Set to markers only
	        model.setSeriesLine(op.getName(), true);
	        model.setSeriesMarker(op.getName(), true);
	        i++;
	    }

		CoordSystem coord = new CoordSystem(model);
		plotPanel = new ChartPanel(model, "Ostatnie " + PLOT_HISTORY + " miesięcy");
		plotPanel.setCoordSystem(coord);
		MultiScatterChartRenderer renderer = new MultiScatterChartRenderer(coord,model);
		renderer.setAllowBuffer(false);
		plotPanel.addChartRenderer(new MultiScatterChartRenderer(coord,model), 0);
			
		plotPanel.setMinimumSize(new Dimension(plotHostPanel.getWidth(), plotHostPanel.getHeight()));
		plotPanel.setSize(new Dimension(plotHostPanel.getWidth(), plotHostPanel.getHeight()));
		plotPanel.setPreferredSize(new Dimension(plotHostPanel.getWidth(), plotHostPanel.getHeight()));
		   /* chartPanel.setPreferredSize(new Dimension(300,300));
		    chartPanel.setMinimumSize(new Dimension(300,300));
		    chartPanel.setSize(new Dimension(300,300));*/
		
		plotHostPanel.removeAll();
		plotHostPanel.add(plotPanel);
		plotHostPanel.repaint();
	}
	
	
	private void initChart()
	{
		Map<String, Integer> op_count = market_agent.getOperatorsCount();
		chart_data = new int[op_count.size()][1];
		refreshChart();
		
		String[] column_id = {"Operators"};
		String[] names = new String[op_count.size()];
		op_count.keySet().toArray(names);
		ObjectChartDataModel chartModel = new ObjectChartDataModel(chart_data, column_id, names);
		
		chartPanel = new ChartPanel(chartModel, "Porównianie");

			CoordSystem coord = new CoordSystem(chartModel);
			BarChartRenderer br = new BarChartRenderer(coord, chartModel);
			
			chartPanel.addChartRenderer(br, 0);
			coord.setPaintAxes(false);
			chartPanel.setCoordSystem(coord);
			
			//chartModel.setManualScale(true);
			chartModel.setMinimumValue(new Double(0.0));
			chartModel.setMaximumValue(new Double(market_agent.getClientsSize()));
			
			chartPanel.setMinimumSize(new Dimension(chartHostPanel.getWidth(), chartHostPanel.getHeight()));
			chartPanel.setSize(new Dimension(chartHostPanel.getWidth(), chartHostPanel.getHeight()));
			chartPanel.setPreferredSize(new Dimension(chartHostPanel.getWidth(), chartHostPanel.getHeight()));
		   /* chartPanel.setPreferredSize(new Dimension(300,300));
		    chartPanel.setMinimumSize(new Dimension(300,300));
		    chartPanel.setSize(new Dimension(300,300));*/
		
		    chartHostPanel.removeAll();
		    chartHostPanel.add(chartPanel);
		    chartHostPanel.repaint();
	}
	
	private void refreshChart()
	{
		Map<String, Integer> op_count = market_agent.getOperatorsCount();
		int i = 0;
		for (String op : op_count.keySet())
		{
			chart_data[i][0] = op_count.get(op);
			i++;
		}
	}

	private void initializeData(Integer nr_of_clients) {
		// / set print level to speed things up
		market_agent = new MarketAgent();
		market_agent.initializeData(nr_of_clients);
		IO.init(IO.INFO, operatorPrints, clientPrints);

		/*int i = 0;
		Map<String, Integer> op_count = market_agent.getOperatorsCount();
		operatorsLabels = new JLabel[op_count.size() * 2];
		for (String op : op_count.keySet()) {
			operatorsLabels[i] = new JLabel(op);
			//operatorsPane.add(operatorsLabels[i]);
			i++;

			operatorsLabels[i] = new JLabel(Integer.toString(op_count.get(op)));
			operatorsPane.add(operatorsLabels[i]);
			i++;
		}*/
		
		operatorsTableModel = new MyTableModel();
		operatorsTable.setModel(operatorsTableModel);
		
		initChart();
	    initPlot();
		// pack();
	}

	public void run()
	{
		mainThread.start();
	}
	
	private class mainLoop implements Runnable{
	public void run() {
		

		while (true) 
		{
			long currentTimeInSeconds = System.currentTimeMillis() / 1000;
			market_agent.doMonthOperations();

			// sort operators based on user number
			ArrayList<Operator> ops = new ArrayList<Operator>(market_agent.getOperators().values());
			Collections.sort(ops, new Comparator<Object>() {
				public int compare(Object left, Object right) {
					Integer leftKey = ((Operator) left).getClientCount();
					Integer rightKey = ((Operator) right).getClientCount();

					return (-1) * leftKey.compareTo(rightKey);
				}
			});

			ListIterator<Operator> iOps = ops.listIterator();
			for (int i = 0; iOps.hasNext(); i++) {
				Operator op = iOps.next();

				IO.operatorPrint("\nRozpatrujemy operatora: " + op.getName(),
						IO.DEBUG);
				op.endPeriod(market_agent.getClientsSize(), i + 1, ops.get(0).getPrices());
			}

			// update GUI
			/*for (int i = 0; i < operatorsLabels.length; i += 2) {
				operatorsLabels[i + 1].setText(Integer.toString(market_agent.getOperators().get(
						operatorsLabels[i].getText()).getClientCount()));
			}*/

			IO.envPrint("Time: " + Double.toString(System.currentTimeMillis()/ 1000 - currentTimeInSeconds), 1);

			initChart();
		    initPlot();
		    
			operatorsTableModel.reload();
			operatorsTable.getRowSorter().allRowsChanged();
			operatorsTable.repaint();
			
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	}
	
	public static void main(String args[]) {
		// JFrame frame = new JFrame("FrameDemo");
		EnviromentAgent env = new EnviromentAgent();
		env.initializeData(5000);
		env.setVisible(true);
		env.run();
	}
}
